home *** CD-ROM | disk | FTP | other *** search
/ PC Plus SuperCD (UK) 1997 May / PC Plus Super CD Issue 127 (May 1997).iso / delphi2 / delphite.exe / data.z / DIROUTLN.PAS < prev    next >
Encoding:
Pascal/Delphi Source File  |  1996-08-12  |  9.3 KB  |  339 lines

  1. unit DirOutln;
  2.  
  3. { Directory outline component }
  4.  
  5. interface
  6.  
  7. uses Classes, Forms, Controls, Outline, SysUtils, Graphics, Grids, StdCtrls,
  8.      Menus;
  9.  
  10. type
  11.   TTextCase = (tcLowerCase, tcUpperCase, tcAsIs);
  12.   TCaseFunction = function(const AString: string): string;
  13.  
  14.   TDirectoryOutline = class(TCustomOutline)
  15.   private
  16.     FDrive: Char;
  17.     FDirectory: TFileName;
  18.     FOnChange: TNotifyEvent;
  19.     FTextCase: TTextCase;
  20.     FCaseFunction: TCaseFunction;
  21.   protected
  22.     procedure SetDrive(NewDrive: Char);
  23.     procedure SetDirectory(const NewDirectory: TFileName);
  24.     procedure SetTextCase(NewTextCase: TTextCase);
  25.     procedure AssignCaseProc;
  26.     procedure BuildOneLevel(RootItem: Longint); virtual;
  27.     procedure BuildTree; virtual;
  28.     procedure BuildSubTree(RootItem: Longint); virtual;
  29.     procedure Change; virtual;
  30.     procedure Click; override;
  31.     procedure CreateWnd; override;
  32.     procedure Expand(Index: Longint); override;
  33.     procedure Loaded; override;
  34.     procedure WalkTree(const Dest: string);
  35.   public
  36.     constructor Create(AOwner: TComponent); override;
  37.     function ForceCase(const AString: string): string;
  38.     property Drive: Char  read FDrive write SetDrive;
  39.     property Directory: TFileName  read FDirectory write SetDirectory;
  40.     property Lines stored False;
  41.   published
  42.     property Align;
  43.     property BorderStyle;
  44.     property Color;
  45.     property Ctl3D;
  46.     property DragCursor;
  47.     property DragMode;
  48.     property Enabled;
  49.     property Font;
  50.     property ItemHeight;
  51.     property OnChange: TNotifyEvent  read FOnChange write FOnChange;
  52.     property OnClick;
  53.     property OnCollapse;
  54.     property OnDblClick;
  55.     property OnDragDrop;
  56.     property OnDragOver;
  57.     property OnDrawItem;
  58.     property OnEndDrag;
  59.     property OnEnter;
  60.     property OnExit;
  61.     property OnExpand;
  62.     property OnKeyDown;
  63.     property OnKeyPress;
  64.     property OnKeyUp;
  65.     property OnMouseDown;
  66.     property OnMouseMove;
  67.     property OnMouseUp;
  68.     property Options default [ooStretchBitmaps, ooDrawFocusRect];
  69.     property ParentColor;
  70.     property ParentCtl3D;
  71.     property ParentFont;
  72.     property ParentShowHint;
  73.     property PictureClosed;
  74.     property PictureLeaf;
  75.     property PictureOpen;
  76.     property PopupMenu;
  77.     property ScrollBars;
  78.     property Style;
  79.     property ShowHint;
  80.     property TabOrder;
  81.     property TabStop;
  82.     property TextCase: TTextCase  read FTextCase write SetTextCase default tcLowerCase;
  83.     property Visible;
  84.   end;
  85.  
  86. function SameLetter(Letter1, Letter2: Char): Boolean;
  87.  
  88.  
  89. implementation
  90.  
  91. const
  92.   InvalidIndex = -1;
  93.  
  94. constructor TDirectoryOutline.Create(AOwner: TComponent);
  95. begin
  96.   inherited Create(AOwner);
  97.   PictureLeaf := PictureClosed;
  98.   Options := [ooStretchBitmaps, ooDrawFocusRect];
  99.   TextCase := tcLowerCase;
  100.   AssignCaseProc;
  101. end;
  102.  
  103. procedure TDirectoryOutline.AssignCaseProc;
  104. begin
  105.   case TextCase of
  106.     tcLowerCase: FCaseFunction := AnsiLowerCase;
  107.     tcUpperCase: FCaseFunction := AnsiUpperCase;
  108.     else FCaseFunction := nil;
  109.   end;
  110. end;
  111.  
  112. type
  113.   PNodeInfo = ^TNodeInfo;
  114.   TNodeInfo = record
  115.     RootName: TFileName;
  116.     SearchRec: TSearchRec;
  117.     DosError: Integer;
  118.     RootNode: TOutlineNode;
  119.     TempChild, NewChild: Longint;
  120.   end;
  121.  
  122. procedure TDirectoryOutline.BuildOneLevel(RootItem: Longint);
  123. var
  124.   NodeInfo: PNodeInfo;
  125. begin
  126.   New(NodeInfo);
  127.   try
  128.     with NodeInfo^ do
  129.     begin
  130.       RootName := Items[RootItem].FullPath;
  131.       if RootName[Length(RootName)] <> '\' then
  132.         RootName := Concat(RootName, '\');
  133.       RootName := Concat(RootName, '*.*');
  134.       DosError := FindFirst(RootName, faDirectory, SearchRec);
  135.       while DosError = 0 do
  136.       begin
  137.         if (SearchRec.Attr and faDirectory = faDirectory) and (SearchRec.Name[1] <> '.') then
  138.         begin
  139.           SearchRec.Name := ForceCase(SearchRec.Name);
  140.           RootNode := Items[RootItem];
  141.           if RootNode.HasItems then { if has children, must alphabetize }
  142.           begin
  143.             TempChild := RootNode.GetFirstChild;
  144.             while (TempChild <> InvalidIndex) and (Items[TempChild].Text < SearchRec.Name) do
  145.               TempChild := RootNode.GetNextChild(TempChild);
  146.             if TempChild <> InvalidIndex then
  147.               NewChild := Insert(TempChild, SearchRec.Name)
  148.             else NewChild := Add(RootNode.GetLastChild, SearchRec.Name);
  149.           end
  150.           else NewChild := AddChild(RootItem, SearchRec.Name); { if first child, just add }
  151.         end;
  152.         DosError := FindNext(SearchRec);
  153.       end;
  154.     end;
  155.     Items[RootItem].Data := Pointer(1); { make non-nil so we know we've been here }
  156.   finally
  157.     Dispose(NodeInfo);
  158.   end;
  159. end;
  160.  
  161. procedure TDirectoryOutline.BuildTree;
  162. begin
  163.   Clear;
  164.   AddChild(0, ForceCase(Drive + ':'));
  165.   WalkTree(FDirectory);
  166.   Change;
  167. end;
  168.  
  169. procedure TDirectoryOutline.BuildSubTree(RootItem: Longint);
  170. var
  171.   TempRoot: Longint;
  172.   RootNode: TOutlineNode;
  173. begin
  174.   BuildOneLevel(RootItem);
  175.   RootNode := Items[RootItem];
  176.   TempRoot := RootNode.GetFirstChild;
  177.   while TempRoot <> InvalidIndex do
  178.   begin
  179.     BuildSubTree(TempRoot);
  180.     TempRoot := RootNode.GetNextChild(TempRoot);
  181.   end;
  182. end;
  183.  
  184. procedure TDirectoryOutline.Change;
  185. begin
  186.   if Assigned(FOnChange) then FOnChange(Self);
  187. end;
  188.  
  189. procedure TDirectoryOutline.Click;
  190. begin
  191.   inherited Click;
  192.   Directory := Items[SelectedItem].FullPath;
  193. end;
  194.  
  195. procedure TDirectoryOutline.CreateWnd;
  196. var
  197.   CurrentPath: string;
  198. begin
  199.   inherited CreateWnd;
  200.   if FDrive = #0 then
  201.   begin
  202.     GetDir(0, CurrentPath);
  203.     FDrive := ForceCase(CurrentPath)[1];
  204.     FDirectory := ForceCase(CurrentPath);
  205.   end;
  206.   if (not (csLoading in ComponentState)) and
  207.     (csDesigning in ComponentState) then BuildTree;
  208. end;
  209.  
  210. procedure TDirectoryOutline.Expand(Index: Longint);
  211. begin
  212.   if Items[Index].Data = nil then { if we've not previously expanded }
  213.     BuildOneLevel(Index);
  214.   inherited Expand(Index); { call the event handler }
  215. end;
  216.  
  217. function TDirectoryOutline.ForceCase(const AString: string): string;
  218. begin
  219.   if Assigned(FCaseFunction) then
  220.     Result := FCaseFunction(AString)
  221.   else Result := AString;
  222. end;
  223.  
  224. procedure TDirectoryOutline.Loaded;
  225. begin
  226.   inherited Loaded;
  227.   AssignCaseProc;
  228.   BuildTree;
  229. end;
  230.  
  231. procedure TDirectoryOutline.SetDirectory(const NewDirectory: TFileName);
  232. var
  233.   TempPath: TFileName;
  234. begin
  235.   if Length(NewDirectory) > 0 then  { ignore empty directory }
  236.   begin
  237.     TempPath := ForceCase(ExpandFileName(NewDirectory)); { expand to full path }
  238.     if (Length(TempPath) > 3) and (TempPath[Length(TempPath)] = '\') then
  239.       SetLength(TempPath, Length(TempPath) - 1);
  240.     if CompareStr(TempPath, FDirectory) <> 0 then { is it a dir change? }
  241.     begin
  242.       FDirectory := TempPath; { set new directory }
  243.       ChDir(FDirectory); { go there }
  244.       if TempPath[1] <> Drive then { check to see if we changed drives, too }
  245.         Drive := TempPath[1] { change drive/build list if needed }
  246.       else
  247.       begin
  248.         WalkTree(TempPath);
  249.         Change; { otherwise, we're done }
  250.       end;
  251.     end;
  252.   end;
  253. end;
  254.  
  255. procedure TDirectoryOutline.SetDrive(NewDrive: Char);
  256. var
  257.   TempPath: string;
  258. begin
  259.   if UpCase(NewDrive) in ['A'..'Z'] then { disallow all but drive letters}
  260.   begin
  261.     if (FDrive = #0) or not SameLetter(NewDrive, FDrive) then { update if no current drive or change }
  262.     begin
  263.       FDrive := NewDrive;
  264.       ChDir(FDrive + ':');
  265.       GetDir(0, TempPath);
  266.       FDirectory := ForceCase(TempPath); { use correct case }
  267.       if not (csLoading in ComponentState) then BuildTree; { this ends up calling Change }
  268.     end;
  269.   end;
  270. end;
  271.  
  272. procedure TDirectoryOutline.SetTextCase(NewTextCase: TTextCase);
  273. var
  274.   CurrentPath: string;
  275. begin
  276.   if NewTextCase <> FTextCase then
  277.   begin
  278.     FTextCase := NewTextCase;
  279.     AssignCaseProc;
  280.     if NewTextCase = tcAsIs then
  281.     begin
  282.       GetDir(0, CurrentPath);
  283.       FDrive := CurrentPath[1];
  284.       FDirectory := CurrentPath;
  285.     end;
  286.     if not (csLoading in ComponentState) then BuildTree;
  287.   end;
  288. end;
  289.  
  290. procedure TDirectoryOutline.WalkTree(const Dest: string);
  291. var
  292.   TempPath, NextDir: TFileName;
  293.   SlashPos: Integer;
  294.   TempItem: Longint;
  295.  
  296.   function GetChildNamed(const Name: string): Longint;
  297.   begin
  298.     Items[TempItem].Expanded := True;
  299.     Result := Items[TempItem].GetFirstChild;
  300.     while Result <> InvalidIndex do
  301.     begin
  302.       if Items[Result].Text = Name then Exit;
  303.       Result := Items[TempItem].GetNextChild(Result);
  304.     end;
  305.   end;
  306.  
  307. begin
  308.   TempItem := 1; { start at root }
  309.   TempPath := ForceCase(Dest);
  310.   if Pos(':', TempPath) > 0 then
  311.     TempPath := Copy(TempPath, Pos(':', TempPath) + 1, Length(TempPath));
  312.   if TempPath[1] = '\' then System.Delete(TempPath, 1, 1);
  313.   Pos('\', TempPath);
  314.   NextDir := TempPath;
  315.   while Length(TempPath) > 0 do
  316.   begin
  317.     SlashPos := Pos('\', TempPath);
  318.     if SlashPos > 0 then
  319.     begin
  320.       NextDir := Copy(TempPath, 1, SlashPos - 1);
  321.       TempPath := Copy(TempPath, SlashPos + 1, Length(TempPath));
  322.     end
  323.     else
  324.     begin
  325.       NextDir := TempPath;
  326.       TempPath := '';
  327.     end;
  328.     TempItem := GetChildNamed(NextDir);
  329.   end;
  330.   SelectedItem := TempItem;
  331. end;
  332.  
  333. function SameLetter(Letter1, Letter2: Char): Boolean;
  334. begin
  335.   Result := UpCase(Letter1) = UpCase(Letter2);
  336. end;
  337.  
  338. end.
  339.